// pcx.c
// PCX read/write routines
// A bit more flexible this time around, for variant destinations, image sizes,
// etc.

#include <stdio.h>
#include "timer.h"
#include "vga.h"

char manufacturer;                     // pcx header
char version;
char encoding;
char bits_per_pixel;
short xmin;
short ymin;
short xmax;
short ymax;
short hres;
short vres;
char palette[48];
char reserved;
char color_planes;
short int bytes_per_line;
short int palette_type;
char filler[58];

unsigned short width;
unsigned short depth;
unsigned short bytes;
unsigned short i;
unsigned char c;
unsigned char run;
unsigned char ss = 0;
unsigned int vidoffset;
FILE* pcxf;

void ReadPCXLine(unsigned char* dest) {
    int j;
    unsigned int n = 0;

    do {
        c = fgetc(pcxf) & 0xff;
        if ((c & 0xc0) == 0xc0) {
            run = c & 0x3f;
            c = fgetc(pcxf);
            for (j = 0; j < run; j++) {
                dest[vidoffset + n + j] = c;
            }
            n += run;
        } else {
            dest[vidoffset + n] = c;
            n++;
        }
    } while (n < bytes);
}

void LoadPCXHeader(char* fname) {
    pcxf = fopen(fname, "rb");
    if (!pcxf) {
        err("Could not open specified PCX file.");
    }
    fread(&manufacturer, 1, 1, pcxf);
    fread(&version, 1, 1, pcxf);
    fread(&encoding, 1, 1, pcxf);
    fread(&bits_per_pixel, 1, 1, pcxf);
    fread(&xmin, 1, 2, pcxf);
    fread(&ymin, 1, 2, pcxf);
    fread(&xmax, 1, 2, pcxf);
    fread(&ymax, 1, 2, pcxf);
    fread(&hres, 1, 2, pcxf);
    fread(&vres, 1, 2, pcxf);
    fread(&palette, 1, 48, pcxf);
    fread(&reserved, 1, 1, pcxf);
    fread(&color_planes, 1, 1, pcxf);
    fread(&bytes_per_line, 1, 2, pcxf);
    fread(&palette_type, 1, 2, pcxf);
    fread(&filler, 1, 58, pcxf);
    fseek(pcxf, -768L, SEEK_END);
    fread(pal, 1, 768, pcxf);
    fseek(pcxf, 128L, SEEK_SET);
    width = xmax - xmin + 1;
    depth = ymax - ymin + 1;
    bytes = bytes_per_line;

    for (i = 0; i < 768; i++) {
        pal[i] >>= 2;
    }
}

void LoadPCXHeaderNP(char* fname) {
    pcxf = fopen(fname, "rb");
    if (!pcxf) {
        err("Could not open specified PCX file.");
    }
    fread(&manufacturer, 1, 1, pcxf);
    fread(&version, 1, 1, pcxf);
    fread(&encoding, 1, 1, pcxf);
    fread(&bits_per_pixel, 1, 1, pcxf);
    fread(&xmin, 1, 2, pcxf);
    fread(&ymin, 1, 2, pcxf);
    fread(&xmax, 1, 2, pcxf);
    fread(&ymax, 1, 2, pcxf);
    fread(&hres, 1, 2, pcxf);
    fread(&vres, 1, 2, pcxf);
    fread(&palette, 1, 48, pcxf);
    fread(&reserved, 1, 1, pcxf);
    fread(&color_planes, 1, 1, pcxf);
    fread(&bytes_per_line, 1, 2, pcxf);
    fread(&palette_type, 1, 2, pcxf);
    fread(&filler, 1, 58, pcxf);
    width = xmax - xmin + 1;
    depth = ymax - ymin + 1;
    bytes = bytes_per_line;
}

void loadpcx(char* fname, unsigned char* dest) {
    LoadPCXHeader(fname);

    for (i = 0; i < depth; i++) {
        vidoffset = 5648 + (i * 352);
        ReadPCXLine(dest);
    }

    fclose(pcxf);
}

void WritePCXLine(unsigned char* p) {
    int i;
    unsigned char byte;
    unsigned char samect;
    unsigned char repcode;

    i = 0;
    do {
        byte = p[i++];
        samect = 1;
        while (samect < (unsigned)63 && i < 320 && byte == p[i]) {
            samect++;
            i++;
        }
        if (samect > 1 || (byte & 0xC0) != 0) {
            repcode = 0xC0 | samect;
            fwrite(&repcode, 1, 1, pcxf);
        }
        fwrite(&byte, 1, 1, pcxf);
    } while (i < 320);
}

void WritePalette() {
    char b;
    int i;

    for (i = 0; i < 768; i++) {
        pal[i] <<= 2;
    }

    b = 12;
    fwrite(&b, 1, 1, pcxf);
    fwrite(pal, 1, 768, pcxf);

    for (i = 0; i < 768; i++) {
        pal[i] >>= 2;
    }
}

void ScreenShot() {
    unsigned char b1;
    unsigned short w1;
    char fnamestr[13];

    // Takes a snapshot of the current screen.

    dec_to_asciiz(ss, fnamestr);
    b1 = strlen(fnamestr);
    fnamestr[b1++] = '.';
    fnamestr[b1++] = 'P';
    fnamestr[b1++] = 'C';
    fnamestr[b1++] = 'X';
    fnamestr[b1++] = 0;

    pcxf = fopen(fnamestr, "wb");
    ss++;

    // Write PCX header

    b1 = 10;
    fwrite(&b1, 1, 1, pcxf); // manufacturer always = 10
    b1 = 5;
    fwrite(&b1, 1, 1, pcxf);  // version = 3.0, >16 colors
    b1 = 1;
    fwrite(&b1, 1, 1, pcxf);  // encoding always = 1
    b1 = 8;
    fwrite(&b1, 1, 1, pcxf);  // 8 bits per pixel, for 256 colors
    w1 = 0;
    fwrite(&w1, 1, 2, pcxf);  // xmin = 0;
    w1 = 0;
    fwrite(&w1, 1, 2, pcxf);  // ymin = 0;
    w1 = 319;
    fwrite(&w1, 1, 2, pcxf);  // xmax = 319;
    w1 = 199;
    fwrite(&w1, 1, 2, pcxf);  // ymax = 199;
    w1 = 320;
    fwrite(&w1, 1, 2, pcxf);  // hres = 320;
    w1 = 200;
    fwrite(&w1, 1, 2, pcxf);  // vres = 200;

    // 16-color palette data. Who knows what's
    // actually in here. It doesn't matter since
    // the 256-color palette is stored elsewhere.
    fwrite(virscr, 1, 48, pcxf);

    b1 = 0;
    fwrite(&b1, 1, 1, pcxf);   // reserved always = 0.
    b1 = 1;
    fwrite(&b1, 1, 1, pcxf);   // number of color planes. Just 1 for 8bit.
    w1 = 320;
    fwrite(&w1, 1, 2, pcxf); // number of bytes per line

    w1 = 0;
    fwrite(&w1, 1, 1, pcxf);
    fwrite(virscr, 1, 59, pcxf);          // filler

    for (w1 = 0; w1 < 200; w1++) {
        WritePCXLine(screen + (w1 * 320));
    }

    WritePalette();
    fclose(pcxf);
    timer_count = 0;
}
